Skip to content

grpc-protobuf: add server codegen utils - #2818

Open
arjan-bal wants to merge 14 commits into
grpc:masterfrom
arjan-bal:server-protobuf
Open

grpc-protobuf: add server codegen utils#2818
arjan-bal wants to merge 14 commits into
grpc:masterfrom
arjan-bal:server-protobuf

Conversation

@arjan-bal

@arjan-bal arjan-bal commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

This change introduces the types required for server codegen (see this branch for the full set of changes).

Additionally, this PR mvoes the ServerStatus from the grpc crate to the grpc-protobuf crate. ServerStatus wraps the standard status type to prevent users from accidentally forwarding client errors from a server handler using the ? operator.

Finally, this change re-exports the async_trait macro from the grpc crate (similar to tonic). Because async trait implementations require #[async_trait], re-exporting it avoids requiring applications to add async-trait as a direct dependency in their Cargo.toml while using the generated code.

@arjan-bal
arjan-bal force-pushed the server-protobuf branch 2 times, most recently from cfdffb8 to 4640ba4 Compare August 19, 2026 13:48
Comment on lines +244 to +245
/// Creates a new [`ServerStatusError`] from a [`StatusError`].
pub fn from_status(status: StatusError) -> Self {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably document this and into to explain why we didn't impl From instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to the rustdocs of from_status and into_status explaining this.

Comment thread grpc-protobuf/src/server/mod.rs Outdated
Comment on lines +102 to +103
let mut res_view = ProtoRecvMessage::from_mut(req);
self.rx.dyn_next(&mut res_view).await

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: req_view since these are requests instead of responses? (I assume this was copied from the client recv_into which is why it's res?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's a copy/paste error. Fixed.

///
/// Note: success does *not* indicate successful receipt of the response by
/// the client; it only indicates that the stream has not yet terminated.
pub async fn send(&mut self, resp: &impl AsView<Proxied = M>) -> Result<(), ()> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do something to silence clippy here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clippy docs suggest replacing the unit struct () with a custom Err type that conveys what went wrong, for example:

#[derive(Debug)]
pub struct EndOfStream;

impl fmt::Display for EndOfStream {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "End of Stream")
    }
}

I was thinking of adding two types:

  1. EndOfStream: Represents either a graceful termination or failure. This will be used in the send method.
  2. UnexpectedEndOfStream: Represents stream failures. This will be used in therecv method that returns Option<Result<M, ()>> presently, where None already represents graceful termination.

We would need to make similar changes in the client APIs for consistency. What do you think?

Comment thread grpc-protobuf/src/server/bidi.rs Outdated

/// An adapter that wraps a [`BidiStreamingMethod`] to handle incoming
/// bidirectional-streaming RPCs.
pub struct BidiStreamingAdapter<M: BidiStreamingMethod> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're supposed to not specify the types here unless they're used here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, removed the bounds from all the adapters and their impl blocks.

{
return trailers_from_status(Err(ServerStatusError::new(
StatusCodeError::Internal,
"client did not send a request message",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the RPC error we send back to the client?

Maybe "unary call missing request message" or something?

Also should this be using an interceptor to deal with this (and also doing a second recv and expecting to get a None)?

#2602

Comment thread grpc-protobuf/src/server/unary.rs Outdated
)));
}

let mut resp = <M::Response as Default>::default();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT? Let's move this up to where req is defined and // TODO: allocate these together on an arena?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, done.

@dfawley dfawley assigned arjan-bal and unassigned dfawley Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants